package com.nexgo.payment.databases;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;

import com.nexgo.payment.BuildConfig;
import com.nexgo.payment.constrant.CardMode;
import com.nexgo.payment.constrant.OfflineState;
import com.nexgo.payment.constrant.SysConstant;
import com.nexgo.payment.constrant.TransType;
import com.nexgo.payment.constrant.UploadState;
import com.nexgo.payment.constrant.ValueKey;
import com.nexgo.payment.util.StringUtils;

import org.greenrobot.greendao.query.QueryBuilder;
import org.greenrobot.greendao.query.WhereCondition;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by xiaox on 2017/1/17.
 */
public class Repository implements DataSource {
    private DaoSession mDaoSession;
    private static Repository INSTANCE = null;
    private Context mContext;
    private Map<ValueKey, Object> mValueMap;

    private Repository() {
        mValueMap = new HashMap<>();
    }

    public static Repository getInstance() {
        if (INSTANCE == null) {
            synchronized (Repository.class) {
                INSTANCE = new Repository();
            }
        }
        return INSTANCE;
    }

    public void init(Context context) {
        mContext = context.getApplicationContext();
        SQLiteDatabase db = new CustomOpenHelper(new CustomContext(context, SysConstant.DB_PATH), BuildConfig.APPLICATION_ID + "_payment-db", null)
                .getWritableDatabase();
        mDaoSession = new DaoMaster(db).newSession();
        AppParameterManager.getInstance().init(context);
    }

    @Override
    public void setValue(ValueKey key, Object value) {
        mValueMap.put(key, value);
    }

    @Override
    public Object getValue(ValueKey key) {
        return mValueMap.get(key);
    }

    @Override
    public PasswordInfo getPasswordInfo() {
        return mDaoSession.getPasswordInfoDao()
                .queryBuilder()
                .limit(1)
                .orderAsc(PasswordInfoDao.Properties.Id)
                .unique();
    }

    @Override
    public void setPasswordInfo(PasswordInfo info) {
        long count = mDaoSession.getPasswordInfoDao().count();
        if (count == 0) {
            mDaoSession.getPasswordInfoDao().insert(info);
        } else {
            PasswordInfo entity = mDaoSession.getPasswordInfoDao()
                    .queryBuilder().limit(1)
                    .orderAsc(PasswordInfoDao.Properties.Id)
                    .unique();
            //更新管理员密码
            if (!TextUtils.isEmpty(info.getManagePassword())) {
                entity.setManagePassword(info.getManagePassword());
            }
            //更新主管密码
            if (!TextUtils.isEmpty(info.getAdminPassword())) {
                entity.setAdminPassword(info.getAdminPassword());
            }
            //更新安全密码
            if (!TextUtils.isEmpty(info.getSecurityPassword())) {
                entity.setSecurityPassword(info.getSecurityPassword());
            }
            mDaoSession.getPasswordInfoDao().update(entity);
        }
    }

    @Override
    public long getSuccessfulTransRecordNum() {
        return mDaoSession.getTransRecordDao().queryBuilder().where(TransRecordDao.Properties.IsSuccess.eq(true)).count();
    }
    @Override
    public long getReversalTransRecordNum() {
        return mDaoSession.getTransRecordDao().queryBuilder().where(TransRecordDao.Properties.IsNeedReversal.eq(true)).count();
    }
    //lr add
    @Override
    public long getSuccessfulTransRecordNotVoidNum() {
        return mDaoSession.getTransRecordDao().queryBuilder().where(TransRecordDao.Properties.IsSuccess.eq(true),TransRecordDao.Properties.IsVoid.eq(false)).count();
    }

    @Override
    public String getParamValue(String preferencesId) {
        return AppParameterManager.getInstance().getValue(preferencesId);
    }

    @Override
    public void setParamValue(String preferencesId, String value) {
        AppParameterManager.getInstance().setValue(preferencesId, value);
    }

    @Override
    public void syncParamValue() {
        AppParameterManager.getInstance().syncValue();
    }

    @Override
    public void increaseTraceNumber() {
        AppParameterManager.getInstance().increaseTraceNumber();
    }

    @Override
    public void increaseBatchNumber() {
        AppParameterManager.getInstance().increaseBatchNumber();
    }

    @Override
    public long insertTransRecord(TransRecord transRecord) {
        return mDaoSession.getTransRecordDao().insert(transRecord);
    }

    @Override
    public void delTransRecord(String traceNum) {
        TransRecordDao transRecordDao = mDaoSession.getTransRecordDao();
        TransRecord transRecord = transRecordDao.queryBuilder().where(TransRecordDao.Properties.TraceNum.eq(traceNum)).build().unique();
        if (transRecord != null) {
            transRecordDao.delete(transRecord);
        }
    }

    @Override
    public void delLastTransRecord() {
        mDaoSession.getTransRecordDao().deleteByKey(getLastTransRecord().getId());
    }

    @Override
    public void delTotalTransRecord() {
        mDaoSession.getTransRecordDao().deleteAll();
    }

    @Override
    public void updateTransRecord(TransRecord transRecord) {
        mDaoSession.getTransRecordDao().update(transRecord);
    }

    @Override
    public TransRecord queryTransRecord(String traceNum) {
        TransRecordDao transRecordDao = mDaoSession.getTransRecordDao();
        return transRecordDao.queryBuilder()
                .where(TransRecordDao.Properties.TraceNum.eq(StringUtils.leftPad(traceNum, 6, '0')))
                .build()
                .unique();
    }

    @Override
    public TransRecord queryScanTransRecord(String traceCode) {
        TransRecordDao transRecordDao = mDaoSession.getTransRecordDao();
        return transRecordDao.queryBuilder()
                .where(TransRecordDao.Properties.PayTraceCode.eq(traceCode),
                        TransRecordDao.Properties.TransType.eq(TransType.SCAN_SALE.ordinal()))
                .build()
                .unique();
    }

    @Override
    public List<TransRecord> queryListTransRecord(int limit, int offset) {
        TransRecordDao transRecordDao = mDaoSession.getTransRecordDao();
        QueryBuilder<TransRecord> queryBuilder = transRecordDao.queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true))
                .orderDesc(TransRecordDao.Properties.Id);
        //限制返回数目，为0时返回所有
        if (limit > 0) {
            queryBuilder.limit(limit).offset(offset);
        }
        return queryBuilder.build().list();
    }

    @Override
    public List<TransRecord> getSuccessfulTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true))
                .build()
                .list();
    }

    //lr add
    @Override
    public List<TransRecord> getSuccessfulTransRecordsNotVoid() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true), TransRecordDao.Properties.IsVoid.eq(false))
                .build()
                .list();
    }


    @Override
    public List<TransRecord> getIccOfflineDeclinedTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.OfflineState.eq(OfflineState.DECLINED.ordinal()))
                .build()
                .list();
    }

    @Override
    public List<TransRecord> getMagOfflineNeverUploadTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()))
                .whereOr(TransRecordDao.Properties.EntryMode.like("01_"),
                        TransRecordDao.Properties.EntryMode.like("02_"))
                .whereOr(TransRecordDao.Properties.CardMode.eq(CardMode.MANUAL.ordinal()),
                        TransRecordDao.Properties.CardMode.eq(CardMode.SWIPE.ordinal()))
                .build()
                .list();
    }

    @Override
    public List<TransRecord> getIccOfflineNeverUploadTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()),
                        TransRecordDao.Properties.UploadState.eq(UploadState.NONE.ordinal()))
                .whereOr(TransRecordDao.Properties.EntryMode.like("05_"),
                        TransRecordDao.Properties.EntryMode.like("07_"),
                        TransRecordDao.Properties.EntryMode.like("98_"))
                .whereOr(TransRecordDao.Properties.CardMode.eq(CardMode.INSERT.ordinal()),
                        TransRecordDao.Properties.CardMode.eq(CardMode.TAP.ordinal()))
                .build()
                .list();
    }

    @Override
    public List<TransRecord> getOfflineUploadRejectedTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()),
                        TransRecordDao.Properties.UploadState.eq(UploadState.OFFLINE_UPLOAD_REJECTED.ordinal()))
                .build()
                .list();
    }

    @Override
    public List<TransRecord> getOfflineUploadFailedTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()),
                        TransRecordDao.Properties.UploadState.eq(UploadState.OFFLINE_UPLOAD_FAILED.ordinal()))
                .build()
                .list();
    }

    @Override
    public long getOfflineUploadFailedTransRecordNum() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()),
                        TransRecordDao.Properties.UploadState.eq(UploadState.OFFLINE_UPLOAD_FAILED.ordinal()))
                .count();
    }

    @Override
    public long getOfflineUploadRejectedTransRecordNum() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()),
                        TransRecordDao.Properties.UploadState.eq(UploadState.OFFLINE_UPLOAD_REJECTED.ordinal()))
                .count();
    }

    @Override
    public TransRecord getLastTransRecord() {
        return mDaoSession.getTransRecordDao()
                .queryBuilder()
                .orderDesc(TransRecordDao.Properties.Id)
                .limit(1)
                .unique();
    }

    @Override
    public TransRecord getLastSuccessfulTransRecord() {
        return mDaoSession.getTransRecordDao()
                .queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true))
                .orderDesc(TransRecordDao.Properties.Id)
                .limit(1)
                .unique();
    }

    @Override
    public void clearTransRecord() {
        mDaoSession.getTransRecordDao().deleteAll();
        mDaoSession.getSignatureDao().deleteAll();
        mDaoSession.getSettleInfoDao().deleteAll();
        mDaoSession.getClassificationDao().deleteAll();
        mDaoSession.getStatisticsDao().deleteAll();
    }

    @Override
    public void clearReversalRecord() {
        String updateQuery = "UPDATE " + TransRecordDao.TABLENAME
                + " SET " + TransRecordDao.Properties.IsNeedReversal.columnName + "=?";
        TransRecordDao transRecordDao = mDaoSession.getTransRecordDao();
        transRecordDao.getDatabase().execSQL(updateQuery, new Object[]{"0"});
    }

    @Override
    public long saveOperator(OperatorInfo info) {
        if (getOperator(info.getOperatorNo()) != null) {
            return -1;
        }
        return mDaoSession.getOperatorInfoDao().insert(info);
    }

    @Override
    public OperatorInfo getOperator(String operatorNo) {
        if (TextUtils.isEmpty(operatorNo)) {
            return null;
        }
        OperatorInfoDao operatorInfoDao = mDaoSession.getOperatorInfoDao();
        OperatorInfo info = operatorInfoDao.queryBuilder()
                .where(OperatorInfoDao.Properties.OperatorNo.eq(operatorNo))
                .limit(1)
                .build()
                .unique();
        if (info != null) {
            return info;
        }
        return null;
    }

    @Override
    public List<OperatorInfo> getOperatorList() {
        return mDaoSession.getOperatorInfoDao().queryBuilder().build().list();
    }

    @Override
    public void updateOperatorInfo(OperatorInfo info) {
        mDaoSession.getOperatorInfoDao().update(info);
    }

    @Override
    public void deleteOperatorByOperatorNo(String OperatorNo) {
        OperatorInfoDao operatorInfoDao = mDaoSession.getOperatorInfoDao();
        OperatorInfo info = operatorInfoDao.queryBuilder()
                .where(OperatorInfoDao.Properties.OperatorNo.eq(OperatorNo))
                .limit(1)
                .build()
                .unique();
        if (info != null) {
            operatorInfoDao.delete(info);
        }
    }

    @Override
    public long insertBlacklist(Blacklist blacklist) {
        return mDaoSession.getBlacklistDao().insert(blacklist);
    }

    @Override
    public void clearBlacklist() {
        mDaoSession.getBlacklistDao().deleteAll();
    }

    @Override
    public boolean isBlacklist(String pan) {
        BlacklistDao blacklistDao = mDaoSession.getBlacklistDao();
        QueryBuilder<Blacklist> where = blacklistDao.queryBuilder().where(
                new WhereCondition.StringCondition(
                        "instr('" + pan + "', " + BlacklistDao.Properties.CardBin.columnName + ") = 1"
                )
        );
        return where.count() > 0;
    }

    @Override
    public boolean isInQpsBinA(String pan) {
        try {
            boolean isIn = false;
            String format = String.format(SysConstant.LOCALE, "%02d", pan.length()) + pan;
            BufferedReader reader = new BufferedReader(new InputStreamReader(mContext.getAssets().open(SysConstant.QPS_BIN_A)));
            String bin;
            while ((bin = reader.readLine()) != null) {
                if (format.startsWith(bin)) {
                    isIn = true;
                    break;
                }
            }
            reader.close();
            return isIn;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public long insertQpsBinB(QpsBinB qpsBin) {
        return mDaoSession.getQpsBinBDao().insert(qpsBin);
    }

    @Override
    public void clearQpsBinB() {
        mDaoSession.getQpsBinBDao().deleteAll();
    }

    @Override
    public boolean isInQpsBinB(String pan) {
        boolean isIn = false;
        try {
            String format = String.format(SysConstant.LOCALE, "%02d", pan.length()) + pan;
            BufferedReader reader = new BufferedReader(new InputStreamReader(mContext.getAssets().open(SysConstant.QPS_BIN_B)));
            String bin;
            while ((bin = reader.readLine()) != null) {
                if (format.startsWith(bin)) {
                    isIn = true;
                    break;
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!isIn) {
            QpsBinBDao qpsBinBDao = mDaoSession.getQpsBinBDao();
            QueryBuilder<QpsBinB> where = qpsBinBDao.queryBuilder().where(
                    new WhereCondition.StringCondition(
                            "instr('" + String.format(SysConstant.LOCALE, "%02d", pan.length()) + pan + "', " + QpsBinBDao.Properties.CardBin.columnName + ") = 1"
                    )
            );
            isIn = where.count() > 0;
        }
        return isIn;
    }

    @Override
    public long insertQpsBinC(QpsBinC qpsBinC) {
        return mDaoSession.getQpsBinCDao().insert(qpsBinC);
    }

    @Override
    public void clearQpsBinC() {
        mDaoSession.getQpsBinCDao().deleteAll();
    }

    @Override
    public boolean isInQpsBinC(String pan) {
        QpsBinCDao qpsBinCDao = mDaoSession.getQpsBinCDao();
        QueryBuilder<QpsBinC> where = qpsBinCDao.queryBuilder().where(
                new WhereCondition.StringCondition(
                        "instr('" + String.format(SysConstant.LOCALE, "%02d", pan.length()) + pan + "', " + QpsBinCDao.Properties.CardBin.columnName + ") = 1"
                )
        );
        return where.count() > 0;
    }

    @Override
    public long insertSignature(Signature signature) {
        return mDaoSession.getSignatureDao().insertOrReplace(signature);
    }

    @Override
    public Signature getSignature(String traceNum) {
        return mDaoSession.getSignatureDao().queryBuilder()
                .where(SignatureDao.Properties.TraceNum.eq(traceNum))
                .limit(1)
                .build()
                .unique();
    }

    @Override
    public SettleInfo getSettleInfo() {
        return mDaoSession.getSettleInfoDao()
                .queryBuilder()
                .orderDesc(SettleInfoDao.Properties.Id)
                .limit(1)
                .unique();
    }

    @Override
    public void saveSettleInfo(SettleInfo info) {
        mDaoSession.getSettleInfoDao().deleteAll();
        mDaoSession.getStatisticsDao().deleteAll();
        mDaoSession.getClassificationDao().deleteAll();
        info.getStatistics().setId(null);
        long statisticsId = mDaoSession.getStatisticsDao().insert(info.getStatistics());
        info.getInterStatistics().setId(null);
        long interStatisticsId = mDaoSession.getStatisticsDao().insert(info.getInterStatistics());
        info.getClassification().setId(null);
        long classificationId = mDaoSession.getClassificationDao().insert(info.getClassification());
        info.getInterClassification().setId(null);
        long interClassificationId = mDaoSession.getClassificationDao().insert(info.getInterClassification());
        //lr扫码
        info.getScanStatistics().setId(null);
        long scanStatisticsId = mDaoSession.getStatisticsDao().insert(info.getScanStatistics());
        info.getScanClassification().setId(null);
        long scanClassificationId = mDaoSession.getClassificationDao().insert(info.getScanClassification());

        info.setStatisticsId(statisticsId);
        info.setInterStatisticsId(interStatisticsId);
        info.setClassificationId(classificationId);
        info.setInterClassificationId(interClassificationId);
        //lr扫码
        info.setScanStatisticsId(scanStatisticsId);
        info.setScanClassificationId(scanClassificationId);

        mDaoSession.getSettleInfoDao().insert(info);
    }

    @Override
    public void updateSettleInfo(SettleInfo info) {
        mDaoSession.getSettleInfoDao().update(info);
    }

    @Override
    public long getBatchUploadSuccessfulTransRecordNum() {
        return mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsBatchUpload.eq(true))
                .count();
    }

    @Override
    public Signature getLastOnlineButNotCurrentSignature(String traceNum) {
        return mDaoSession.getSignatureDao()
                .queryBuilder()
                .where(SignatureDao.Properties.TraceNum.notEq(traceNum),
                        SignatureDao.Properties.IsOffline.eq(false),
                        SignatureDao.Properties.IsEverUpload.eq(false),
                        SignatureDao.Properties.IsUploadSuccessful.eq(false))
                .orderDesc(SignatureDao.Properties.Id)
                .limit(1)
                .unique();
    }

    @Override
    public List<Signature> getNeverUploadSignatures() {
        return mDaoSession.getSignatureDao()
                .queryBuilder()
                .where(SignatureDao.Properties.IsEverUpload.eq(false),
                        SignatureDao.Properties.IsUploadSuccessful.eq(false))
                .build()
                .list();
    }

    @Override
    public List<Signature> getUploadFailedSignatures() {
        return mDaoSession.getSignatureDao()
                .queryBuilder()
                .where(SignatureDao.Properties.IsUploadSuccessful.eq(false),
                        SignatureDao.Properties.IsEverUpload.eq(true))
                .build()
                .list();
    }

    @Override
    public List<Signature> getOfflineNeverUploadSignatures() {
        return mDaoSession.getSignatureDao()
                .queryBuilder()
                .where(SignatureDao.Properties.IsOffline.eq(true),
                        SignatureDao.Properties.IsEverUpload.eq(false),
                        SignatureDao.Properties.IsUploadSuccessful.eq(false))
                .build()
                .list();
    }

    @Override
    public void updateSignature(Signature signature) {
        mDaoSession.getSignatureDao().update(signature);
    }

    @Override
    public void clearSignature() {
        mDaoSession.getSignatureDao().deleteAll();
    }

    @Override
    public Signature querySignature(String traceNum) {
        return mDaoSession.getSignatureDao().queryBuilder()
                .where(SignatureDao.Properties.TraceNum.eq(traceNum))
                .build()
                .unique();
    }

    @Override
    public long getTotalOfflineNeverUploadNum() {
        long offlineTrans = mDaoSession.getTransRecordDao().queryBuilder()
                .where(TransRecordDao.Properties.IsSuccess.eq(true),
                        TransRecordDao.Properties.IsVoid.eq(false),
                        TransRecordDao.Properties.OfflineState.eq(OfflineState.APPROVE.ordinal()),
                        TransRecordDao.Properties.UploadState.eq(UploadState.NONE.ordinal()))
                .count();
        long offlineSignature = mDaoSession.getSignatureDao().queryBuilder()
                .where(SignatureDao.Properties.IsOffline.eq(true),
                        SignatureDao.Properties.IsEverUpload.eq(false),
                        SignatureDao.Properties.IsUploadSuccessful.eq(false))
                .count();
        return offlineTrans + offlineSignature;
    }

    @Override
    public List<TransRecord> getScriptTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder().where(TransRecordDao.Properties.ScriptTlv.isNotNull()).build().list();
    }

    @Override
    public List<TransRecord> getReversalTransRecords() {
        return mDaoSession.getTransRecordDao().queryBuilder().where(TransRecordDao.Properties.IsNeedReversal.eq(true)).build().list();
    }
}
